using System.Text;
using System.Collections.Generic;

using Microsoft.SharePoint;

namespace RealDolmen.SharePoint.Helpers
{
    public class SPQueryBuilder
    {
        #region Variables
        private List<Branch> branches;
        private List<Field> orderBys; 
        #endregion // Variables

        #region Constructor
        public SPQueryBuilder()
        {
            branches = new List<Branch>();
            orderBys = new List<Field>();
        } 
        #endregion // Constructor

        #region Properties
        public bool HasBranches()
        {
            return branches.Count > 0;
        } 
        #endregion // Properties


        #region To-Methods
        public override string ToString()
        {
            return Compile();
        }

        public SPQuery ToQuery()
        {
            return ToQuery(false);
        }

        public SPQuery ToQuery(bool searchInSubFolders)
        {
            SPQuery query = new SPQuery();
            query.Query = Compile();
            if (searchInSubFolders)
                query.ViewAttributes = "Scope=\"Recursive\"";
            return query;
        } 
        #endregion // To-Methods

        #region Compilation
        public string Compile()
        {
            return string.Format("{0}{1}", CompileOrderBy(), CompileWhere());
        }

        public string CompileWhere()
        {
            string branches = CompileBranches();
            if (string.IsNullOrEmpty(branches))
                return string.Empty;
            else
                return string.Format("<Where>{0}</Where>", branches);

        }

        public string CompileOrderBy()
        {
            if (orderBys.Count > 0)
            {
                StringBuilder sb = new StringBuilder();
                foreach (Field field in orderBys)
                    sb.Append(field);
                return string.Format("<OrderBy>{0}</OrderBy>", sb.ToString());
            }
            else
                return string.Empty;
        }

        public string CompileBranches()
        {
            string temp = string.Empty;
            foreach (Branch branch in branches)
                temp = branch.Compile(temp);

            return temp;
        } 
        #endregion // Compilation


        #region And Methods
        public void And(Clause clause)
        {
            if (!string.IsNullOrEmpty(clause.ToString()))
                branches.Add(new AndBranch(clause));
        }
        public void And(SPQueryBuilder query)
        {
            And(new Clause(query.CompileBranches()));
        }
        public void And(SPView view)
        {
            And(Clause.ExtractClauseInsideWhereBranch(view));
        }
        public void AndEquals(string fieldName, string valueTotest)
        {
            And(new EqualityClause(fieldName, valueTotest));
        }
        public void AndEquals(SPField field, string valueTotest)
        {
            And(new EqualityClause(field.InternalName, valueTotest, field.TypeAsString));
        }
        public void AndContains(string fieldName, string valueTotest)
        {
            And(new ContainsClause(fieldName, valueTotest));
        }
        public void AndContains(SPField field, string valueTotest)
        {
            And(new ContainsClause(field.InternalName, valueTotest, field.TypeAsString));
        } 
        #endregion // And Methods

        #region Or Methods
        public void Or(Clause clause)
        {
            if (!string.IsNullOrEmpty(clause.ToString()))
                branches.Add(new OrBranch(clause));
        }
        public void Or(SPQueryBuilder query)
        {
            Or(new Clause(query.CompileBranches()));
        }
        public void Or(SPView view)
        {
            Or(Clause.ExtractClauseInsideWhereBranch(view));
        }
        public void OrEquals(string fieldName, string valueTotest)
        {
            Or(new EqualityClause(fieldName, valueTotest));
        }
        public void OrEquals(SPField field, string valueTotest)
        {
            Or(new EqualityClause(field.InternalName, valueTotest, field.TypeAsString));
        }
        public void OrContains(string fieldName, string valueTotest)
        {
            Or(new ContainsClause(fieldName, valueTotest));
        }
        public void OrContains(SPField field, string valueTotest)
        {
            Or(new ContainsClause(field.InternalName, valueTotest, field.TypeAsString));
        } 
        #endregion // Or Methods

        #region OrderBy
        public void OrderBy(string fieldName)
        {
            orderBys.Add(new Field(fieldName));
        }
        public void OrderBy(string fieldName, bool ascending)
        {
            orderBys.Add(new Field(fieldName, ascending));
        } 
        #endregion // OrderBy
    }

    #region Clauses
    public class Clause
    {
        private string clause;

        public Clause() { }
        public Clause(string clause)
        {
            this.clause = clause;
        }

        public override string ToString()
        {
            return clause;
        }

        public static Clause ExtractClauseInsideWhereBranch(SPView view)
        {
            string query = view.Query;
            
            string whereClause = string.Empty;
            if (!string.IsNullOrEmpty(query))
            {
                int startIndex = query.IndexOf("<Where>") + 7;
                int endIndex = query.IndexOf("</Where>", startIndex);

                if (startIndex < endIndex)
                    whereClause = query.Substring(startIndex, endIndex - startIndex);
            }

            return new Clause(whereClause);
        }
    }

    public class EqualityClause : Clause
    {
        private string valueToTest;
        private string fieldName;
        private string fieldType;

        public string FieldName
        {
            get { return fieldName; }
            set { fieldName = value; }
        }

        public string ValueToTest
        {
            get { return valueToTest; }
            set { valueToTest = value; }
        }
        public string FieldType
        {
            get { return fieldType; }
            set { fieldType = value; }
        }
        public EqualityClause(string fieldName, string valueTotest, string fieldType)
        {
            FieldName = fieldName;
            ValueToTest = valueTotest;
            FieldType = fieldType;
        }
        public EqualityClause(string fieldName, string valueTotest)
            : this(fieldName, valueTotest, "Text")
        {
        }
        public override string ToString()
        {
            return string.Format("<{3}><FieldRef Name=\"{0}\" /><Value Type=\"{2}\">{1}</Value></{4}>", FieldName, ValueToTest, FieldType, Operator, Operator);
        }
        public virtual string Operator
        {
            get { return "Eq"; }
        }
    }
    public class NeqClause : EqualityClause
    {
        public NeqClause(string fieldName, string valueTotest, string fieldType)
            : base(fieldName, valueTotest, fieldType)
        {}
        public NeqClause(string fieldName, string valueTotest)
            : base(fieldName, valueTotest)
        { }
        public override string Operator
        {
            get
            {
                return "Neq";
            }
        }
    }

    public class ContainsClause : EqualityClause
    {
        public ContainsClause(string fieldName, string valueTotest)
            : base(fieldName, valueTotest)
        { }
        public ContainsClause(string fieldName, string valueTotest, string fieldType)
            : base(fieldName, valueTotest, fieldType)
        { }
        public override string Operator
        {
            get
            {
                return "Contains";
            }
        }
    }

    public class GeqClause : EqualityClause
    {
        public GeqClause(string fieldName, string valueTotest)
            : base(fieldName, valueTotest)
        { }
        public GeqClause(string fieldName, string valueTotest, string fieldType)
            : base(fieldName, valueTotest, fieldType)
        { }
        public override string Operator
        {
            get
            {
                return "Geq";
            }
        }
    }
    public class GtClause : EqualityClause
    {
        public GtClause(string fieldName, string valueTotest)
            : base(fieldName, valueTotest)
        { }
        public GtClause(string fieldName, string valueTotest, string fieldType)
            : base(fieldName, valueTotest, fieldType)
        { }
        public override string Operator
        {
            get
            {
                return "Gt";
            }
        }
    }
    public class LeqClause : EqualityClause
    {
        public LeqClause(string fieldName, string valueTotest)
            : base(fieldName, valueTotest)
        { }
        public LeqClause(string fieldName, string valueTotest, string fieldType)
            : base(fieldName, valueTotest, fieldType)
        { }
        public override string Operator
        {
            get
            {
                return "Leq";
            }
        }
    }
    public class LtClause : EqualityClause
    {
        public LtClause(string fieldName, string valueTotest)
            : base(fieldName, valueTotest)
        { }
        public LtClause(string fieldName, string valueTotest, string fieldType)
            : base(fieldName, valueTotest, fieldType)
        { }
        public override string Operator
        {
            get
            {
                return "Lt";
            }
        }
    }
    #endregion // Clauses


    #region Branches
    public class Branch
    {
        protected Clause clause;

        public Clause Clause
        {
            get { return clause; }
            set { clause = value; }
        }


        public Branch(Clause clause)
        {
            Clause = clause;
        }

        public virtual string Compile(string previous)
        {
            return clause.ToString();
        }
    }

    public class OrBranch : Branch
    {
        public OrBranch(Clause clause) : base(clause) {}

        public override string Compile(string previous)
        {
            if (previous == string.Empty)
                return clause.ToString();
            else
                return string.Format("<Or>{0}{1}</Or>", previous, clause.ToString());
        }

    }
    public class AndBranch : Branch
    {
        public AndBranch(Clause clause) : base(clause) {}

        public override string Compile(string previous)
        {
            if (previous == string.Empty)
                return clause.ToString();
            else
                return string.Format("<And>{0}{1}</And>", previous, clause.ToString());
        }
    }
    #endregion // Branches


    #region Fields
    public class Field
    {
        private bool ascending = true;
        private string fieldName;

        public bool Ascending
        {
            get { return ascending; }
            set { ascending = value; }
        }

        public string FieldName
        {
            get { return fieldName; }
            set { fieldName = value; }
        }

        public Field(string fieldName)
        {
            FieldName = fieldName;
        }
        public Field(string fieldName, bool ascending) : this(fieldName)
        {
            Ascending = ascending;
        }
        public override string ToString()
        {
            if(! Ascending)
                return string.Format("<FieldRef Name=\"{0}\" Ascending=\"False\" />", FieldName);
            else
                return string.Format("<FieldRef Name=\"{0}\" />", FieldName);
        }
    }
	#endregion // Fields
}
